package com.colin.core;

import com.colin.annotation.LoginCheck;
import com.colin.config.PathCollectProperties;
import lombok.Getter;
import org.apache.catalina.LifecycleState;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.scanners.Scanners;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 当前这个类在项目运行时 IOC容器中是存在这个类一个Spring Bean对象
 */
public class PathCollect {

    /**
     * 这个properties对象内部存在一个字段 controllerPath 是使用这个依赖的开发人员配置的
     */
    private final PathCollectProperties properties;
    private final Reflections reflections;

    @Getter
    private final List<String> loginCheckPath;

    public PathCollect(PathCollectProperties properties) {
        this.properties = properties;
        System.out.println("当前项目通过application.yml配置的controller路径为 : " + properties.getControllerPath());
        this.reflections = this.createReflections();
        this.loginCheckPath = this.getResult();
    }

    /**
     * 如何基于controller的路径 拿到内部的全部controller类呢？
     *
     * 去扫描编译后的文件夹 找到匹配的controller路径的文件夹 然后通过File找到文件夹下的所有类
     *
     * 0720 1010两个班级中 当你要做一个事儿 目的已经清除了 在没有老师的情况下如何自己相办法实现
     * websocket
     */
    private Reflections createReflections() {
        return new Reflections(new ConfigurationBuilder()
                // 扫描项目中的controller包路径
                .forPackage(this.properties.getControllerPath())
                .setScanners(
                        Scanners.SubTypes,
                        Scanners.TypesAnnotated,
                        Scanners.MethodsAnnotated
                )
        );
    }

    /**
     * 由这个方法来触发获取所有路径的逻辑
     * @return
     */
    private List<String> getResult() {
        Map<Class<?>, List<String>> classAndPrimaryPathMap = this.getPrimaryPath();
        List<String> result = new ArrayList<>();

        for (Class<?> controllerClass : classAndPrimaryPathMap.keySet()) {
            List<String> subPath = this.getSubPath(controllerClass);
            // param1: 一级路径集合 param2：当前类子级路径集合
            List<String> fullPath = this.getFullPath(classAndPrimaryPathMap.get(controllerClass), subPath);
            result.addAll(fullPath);
        }
        return result;
    }

    /**
     * 获取一级路径
     *
     * 如果这个开发人员有习惯在controller类上使用@RequestMapping注解定义当前controller的路径前缀
     * 那么这个方法就是统计这个的
     * @return
     */
    private Map<Class<?>, List<String>> getPrimaryPath() {
        // 可以得到 当前扫描包 的路径下 所有类上带有 RequestMapping注解的类的集合RequestMapping
        Set<Class<?>> includeRequestMappingClassSet = this.reflections.getTypesAnnotatedWith(RequestMapping.class);

        Map<Class<?>, List<String>> classAndPrimaryPathMap = new HashMap<>();

        // 遍历所有包含RequestMapping注解的controllerClass 反射获取一级路径
        for (Class<?> controllerClass : includeRequestMappingClassSet) {
            List<String> primaryPathList = new ArrayList<>();
            RequestMapping annotation = controllerClass.getDeclaredAnnotation(RequestMapping.class);
            String[] value = annotation.value();
            // 得到当前类的一级路径
            Collections.addAll(primaryPathList, value);
            classAndPrimaryPathMap.put(controllerClass, primaryPathList);
        }

        return classAndPrimaryPathMap;
    }


    /**
     * 获取二级路径
     * @param controllerClass
     * @return
     */
    private List<String> getSubPath(Class<?> controllerClass) {
        Method[] methods = controllerClass.getDeclaredMethods();

        // 二级路径 和 请求方法 按照如下格式 拼接出来的集合 subPath:requestMethod
        List<String> subPathAndMethodList = new ArrayList<>();

        for (Method method : methods) {
            method.setAccessible(true);
            LoginCheck loginCheck = method.getDeclaredAnnotation(LoginCheck.class);
            // 如果当前方法上不存在LoginCheck注解 以为着当前方法无需登录验证 继续循环
            if (loginCheck == null) {
                continue;
            }

            RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
            GetMapping getMapping = method.getDeclaredAnnotation(GetMapping.class);
            PostMapping postMapping = method.getDeclaredAnnotation(PostMapping.class);
            DeleteMapping deleteMapping = method.getDeclaredAnnotation(DeleteMapping.class);
            PutMapping putMapping = method.getDeclaredAnnotation(PutMapping.class);

            /**
             * requestMapping使用意味着 所有请求方法可以访问
             * 当时不排除用户使用method属性 定义允许访问的请求方法
             */
            if (requestMapping != null) {
//                String method = "*";

                // ["GET", "PUT", "POST", "DELETE"]
                RequestMethod[] requestMethods = requestMapping.method();
                List<String> requestMethodStrList = new ArrayList<>();
                if (requestMethods.length > 0) {
                    // 遍历RequestMethod数组 查看用户限制了那儿写请求方法
                    // @RequestMapping(method = {RequestMethod.POST, RequestMethod.GET})
                    // requestMethods数组 就是 {RequestMethod.POST, RequestMethod.GET}
                    // 每一次循环拿到的 requestMethod 就是 RequestMethod.POST 或者  RequestMethod.GET
                    // requestMethod.name() 就是字符串的请求方法名称
                    for (RequestMethod requestMethod : requestMethods) {
                        String methodName = requestMethod.name();
                        requestMethodStrList.add(methodName);
                    }
                }

                // 我们让需要登录的二级路径 以这样的格式 拼接成字符串
                // subPath:requestMethod
                // /user/1:GET
                // /user/1:POST
                // 上述两个是需要登录验证的 但是如果发送 DELETE或者PUT 请求 /user/1 是不会要求登录的

                String[] subPathArr = requestMapping.value();

                if (subPathArr.length == 0) {
                    for (String requestMethod : requestMethodStrList) {
                        String subPathAndMethod = ":" + requestMethod;
                        subPathAndMethodList.add(subPathAndMethod);
                    }
                } else {
                    // 这个循环走完 当前接口方法上@RequestMapping注解的 uri 和 method就统计完成了 在subPathAndMethodList中
                    for (String subPath : subPathArr) {
                        for (String requestMethod : requestMethodStrList) {
                            String subPathAndMethod = subPath + ":" + requestMethod;
                            subPathAndMethodList.add(subPathAndMethod);
                        }
                    }
                }


            }
            if (getMapping != null) {
                String requestMethod = "GET";

                String[] subPathArr = getMapping.value();

                if (subPathArr.length == 0) {
                    String subPathAndMethod = ":" + requestMethod;
                    subPathAndMethodList.add(subPathAndMethod);
                } else {
                    for (String subPath : subPathArr) {
                        String subPathAndMethod = subPath + ":" + requestMethod;
                        subPathAndMethodList.add(subPathAndMethod);
                    }
                }


            }
            if (postMapping != null) {
                String requestMethod = "POST";

                String[] subPathArr = postMapping.value();
                if (subPathArr.length == 0) {
                    String subPathAndMethod = ":" + requestMethod;
                    subPathAndMethodList.add(subPathAndMethod);
                } else {
                    for (String subPath : subPathArr) {
                        String subPathAndMethod = subPath + ":" + requestMethod;
                        subPathAndMethodList.add(subPathAndMethod);
                    }
                }
            }
            if (deleteMapping != null) {
                String requestMethod = "DELETE";

                String[] subPathArr = deleteMapping.value();
                if (subPathArr.length == 0) {
                    String subPathAndMethod = ":" + requestMethod;
                    subPathAndMethodList.add(subPathAndMethod);
                } else {
                    for (String subPath : subPathArr) {
                        String subPathAndMethod = subPath + ":" + requestMethod;
                        subPathAndMethodList.add(subPathAndMethod);
                    }
                }
            }
            if (putMapping != null) {
                String requestMethod = "PUT";

                String[] subPathArr = putMapping.value();
                if (subPathArr.length == 0) {
                    String subPathAndMethod = ":" + requestMethod;
                    subPathAndMethodList.add(subPathAndMethod);
                } else {
                    for (String subPath : subPathArr) {
                        String subPathAndMethod = subPath + ":" + requestMethod;
                        subPathAndMethodList.add(subPathAndMethod);
                    }
                }
            }
        }
        return subPathAndMethodList;
    }


    /**
     * 统计全路径 + 请求方法
     * @param primaryPathList
     * @param subPathAndRequestMethodList
     * @return
     */
    private List<String> getFullPath(List<String> primaryPathList, List<String> subPathAndRequestMethodList){
        List<String> fullPathList = new ArrayList<>();
        // /user  /person
        for (String primaryPath : primaryPathList) {
            for (String subPathAndRequestMethod : subPathAndRequestMethodList) {
                // /user + /auth:POST
                String fullPath = primaryPath + subPathAndRequestMethod;
                fullPathList.add(fullPath);
            }
        }
        return fullPathList;
    }

}
